
package io.renren.modules.oatools.utils.http;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson.JSON;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.Script;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.logging.log4j.util.Strings;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;

/**
 * OA系统HTTP请求工具类
 * 提供同步/异步HTTP请求功能，支持Groovy脚本自定义请求参数
 */
public class OAHttpRequestUtils {
    private final static Logger log = LoggerFactory.getLogger(OAHttpRequestUtils.class);

    // 使用 ThreadLocal 管理重试次数，避免多线程问题
    private static final ThreadLocal<Integer> serversLoadTimes = ThreadLocal.withInitial(() -> 0);
    private static final int DEFAULT_MAX_RETRY_TIMES = 3;

    // 可重试的HTTP状态码
    private static final Set<Integer> RETRYABLE_STATUS_CODES = Set.of(408, 429, 500, 502, 503, 504);

    // Groovy脚本缓存
    private static final Map<String, Class<?>> scriptCache = new ConcurrentHashMap<>();

    // 使用单例 OkHttpClient 实例，避免频繁创建导致的性能问题
    private static volatile OkHttpClient sharedClient;
    private static final Object clientLock = new Object();

    /**
     * 获取共享的OkHttpClient实例
     * @return OkHttpClient实例
     */
    private static OkHttpClient getSharedClient() {
        if (sharedClient == null) {
            synchronized (clientLock) {
                if (sharedClient == null) {
                    sharedClient = new OkHttpClient.Builder()
                            .connectTimeout(10, TimeUnit.SECONDS)
                            .readTimeout(30, TimeUnit.SECONDS)
                            .writeTimeout(30, TimeUnit.SECONDS)
                            .sslSocketFactory(getSSLSocketFactory(), getX509TrustManager())
                            .hostnameVerifier(getHostnameVerifier())
                            .build();
                }
            }
        }
        return sharedClient;
    }

    /**
     * 获取SSLSocketFactory用于HTTPS请求
     * @return SSLSocketFactory实例
     */
    public static SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, getTrustManager(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            log.error("获取SSLSocketFactory失败", e);
            return null;
        }
    }

    /**
     * 获取TrustManager用于SSL证书验证
     * @return TrustManager数组
     */
    private static TrustManager[] getTrustManager() {
        return new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                        // 客户端证书验证，此处为空实现表示跳过验证
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                        // 服务端证书验证，此处为空实现表示跳过验证
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        // 返回可接受的证书颁发者列表
                        return new X509Certificate[]{};
                    }
                }
        };
    }

    /**
     * 获取HostnameVerifier用于主机名验证
     * @return HostnameVerifier实例
     */
    public static HostnameVerifier getHostnameVerifier() {
        // 返回一个始终验证通过的主机名验证器
        return (s, sslSession) -> true;
    }

    /**
     * 获取X509TrustManager实例
     * @return X509TrustManager实例
     */
    public static X509TrustManager getX509TrustManager() {
        X509TrustManager trustManager = null;
        try {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init((KeyStore) null);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
            }
            trustManager = (X509TrustManager) trustManagers[0];
        } catch (Exception e) {
            log.error("获取X509TrustManager失败", e);
        }

        return trustManager;
    }

    /**
     * 发送 HTTP 请求（主入口方法）
     *
     * @param upResponseBody 上游返回的body
     * @param requestSend    本次请求的参数
     * @return 响应结果
     * @throws IOException IO异常
     */
    public static Map<String, Object> requestSend(String upResponseBody, HttpRequestSend requestSend) throws IOException {
        log.info("开始发送HTTP请求: {}", JSON.toJSONString(requestSend));

        // 参数校验
        validateRequestSend(requestSend);

        String requestUrl = requestSend.getRequestUrl() != null ? requestSend.getRequestUrl() : "{}";
        String requestParams = requestSend.getRequestParams() != null ? requestSend.getRequestParams() : "{}";
        String paramsGroovy = requestSend.getParamsGroovy();
        String requestHeaders = requestSend.getRequestHeaders() != null ? requestSend.getRequestHeaders() : "{}";
        String headersGroovy = requestSend.getHeadersGroovy();
        String bodyGroovy = requestSend.getBodyGroovy();
        String requestClientGrooby = requestSend.getRequestClientGrooby();
        String requestBeforeGroovy = requestSend.getRequestBeforeGroovy();
        String requestAfterGroovy = requestSend.getRequestAfterGroovy();
        String reqBody = requestSend.getRequestBody() != null ? requestSend.getRequestBody() : "{}";
        String requestType = requestSend.getRequestType() != null ? requestSend.getRequestType() : "POST";
        int isAsyn = requestSend.getIsAsyn() != null ? Integer.valueOf(requestSend.getIsAsyn().toString()) : 0;

        // 构建 URL
        HttpUrl url = buildUrl(requestUrl, requestParams, paramsGroovy, upResponseBody, reqBody);

        // 构建请求头
        Headers headers = buildHeaders(requestHeaders, headersGroovy, upResponseBody, reqBody);

        log.info("{} <<<<<<请求地址>>>>>>>>> {}", headers, url);

        // 构建 OkHttpClient
        OkHttpClient client = buildClient(requestClientGrooby, upResponseBody, requestSend);

        // 构建请求体
        RequestBody body = buildRequestBody(reqBody, bodyGroovy, upResponseBody, url, headers);

        // 构建请求
        Request request = buildRequest(url, headers, body, requestType, requestBeforeGroovy, upResponseBody);

        // 重置重试次数
        serversLoadTimes.set(0);

        // 根据 isAsyn 参数决定使用同步还是异步请求
        if (isAsyn > 0) {
            // 异步请求，但无法返回结果给调用方（原设计缺陷）
            executeAsyncRequest(client, request, new HashMap<>(), url, upResponseBody, requestAfterGroovy);
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("url", url);
            result.put("message", "异步请求已发送");
            return result;
        } else {
            // 同步请求
            return executeSyncRequest(client, request, new HashMap<>(), url, upResponseBody, requestAfterGroovy);
        }
    }

    /**
     * 构建 URL
     * @param requestUrl 基础URL
     * @param requestParams 请求参数
     * @param paramsGroovy 参数Groovy脚本
     * @param upResponseBody 上游响应体
     * @param reqBody 请求体
     * @return 构建好的HttpUrl
     */
    private static HttpUrl buildUrl(String requestUrl, String requestParams, String paramsGroovy,
                                    String upResponseBody, String reqBody) {
        HttpUrl.Builder urlBuilder = HttpUrl.parse(requestUrl).newBuilder();
        Map returnParamsMap = new HashMap();

        if (Strings.isNotEmpty(paramsGroovy)) {
            Object[] executeParamsGroovy = new Object[]{upResponseBody, JSON.parseObject(requestParams, Map.class), reqBody};
            Object executeParamsResult = executeGroovy(executeParamsGroovy, paramsGroovy);
            returnParamsMap = (Map) executeParamsResult;
        } else {
            returnParamsMap = JSON.parseObject(requestParams, Map.class);
        }

        returnParamsMap.forEach((key, value) -> {
            urlBuilder.addQueryParameter(key.toString(), value.toString()).build();
            log.debug("{} <<<<<<<>>>>> {}", key, value);
        });

        return urlBuilder.build();
    }

    /**
     * 构建请求头
     * @param requestHeaders 基础请求头
     * @param headersGroovy 请求头Groovy脚本
     * @param upResponseBody 上游响应体
     * @param reqBody 请求体
     * @return 构建好的Headers
     */
    private static Headers buildHeaders(String requestHeaders, String headersGroovy,
                                        String upResponseBody, String reqBody) {
        Headers.Builder headersBuilder = new Headers.Builder();
        Map headersMap = JSON.parseObject(requestHeaders, Map.class);

        if (Strings.isNotEmpty(headersGroovy)) {
            Object[] executeHeadersGroovy = new Object[]{upResponseBody, headersMap, reqBody};
            Object executeHeadersResult = executeGroovy(executeHeadersGroovy, headersGroovy);
            headersMap = (Map) executeHeadersResult;
        }

        headersMap.forEach((key, value) -> {
            headersBuilder.add(key.toString(), value.toString());
            log.debug("{} 《《《《《《《headers》》》》》》》》 {}", key.toString(), value.toString());
        });

        return headersBuilder.build();
    }

    /**
     * 构建 OkHttpClient
     * @param requestClientGrooby 客户端配置Groovy脚本
     * @param upResponseBody 上游响应体
     * @param requestSend 请求参数
     * @return 构建好的OkHttpClient
     */
    private static OkHttpClient buildClient(String requestClientGrooby, String upResponseBody,
                                            HttpRequestSend requestSend) {
        OkHttpClient.Builder clientBuilder;

        if (Strings.isNotEmpty(requestClientGrooby)) {
            Object[] executeClientGroovy = new Object[]{upResponseBody};
            clientBuilder = (OkHttpClient.Builder) executeGroovy(executeClientGroovy, requestClientGrooby);
        } else {
            // 使用共享的客户端配置
            clientBuilder = getSharedClient().newBuilder();
        }

        return clientBuilder
                .sslSocketFactory(getSSLSocketFactory(), getX509TrustManager())
                .hostnameVerifier(getHostnameVerifier())
                .addInterceptor(new OAHttpNetworkInvokeIntercepter(requestSend))
                .build();
    }

    /**
     * 构建请求体
     * @param reqBody 基础请求体
     * @param bodyGroovy 请求体Groovy脚本
     * @param upResponseBody 上游响应体
     * @param url 请求URL
     * @param headers 请求头
     * @return 构建好的RequestBody
     */
    private static RequestBody buildRequestBody(String reqBody, String bodyGroovy,
                                                String upResponseBody, HttpUrl url, Headers headers) {
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, reqBody);

        if (Strings.isNotEmpty(bodyGroovy)) {
            Object[] executeBodyGroovy = new Object[]{upResponseBody, url, reqBody, headers};
            body = (RequestBody) executeGroovy(executeBodyGroovy, bodyGroovy);
        }

        return body;
    }

    /**
     * 构建请求
     * @param url 请求URL
     * @param headers 请求头
     * @param body 请求体
     * @param requestType 请求类型(POST/GET)
     * @param requestBeforeGroovy 请求前Groovy脚本
     * @param upResponseBody 上游响应体
     * @return 构建好的Request
     */
    private static Request buildRequest(HttpUrl url, Headers headers, RequestBody body,
                                        String requestType, String requestBeforeGroovy,
                                        String upResponseBody) {
        Request.Builder requestBuilder = new Request.Builder();

        if (Strings.isNotEmpty(requestBeforeGroovy)) {
            Object[] executeBeforeGroovy = new Object[]{upResponseBody, url, body, headers};
            requestBuilder = (Request.Builder) executeGroovy(executeBeforeGroovy, requestBeforeGroovy);
        }

        Request request;
        if (requestType.equals("POST")) {
            request = requestBuilder.url(url).post(body).headers(headers).build();
        } else if (requestType.equals("GET")) {
            request = requestBuilder.url(url).get().headers(headers).build();
        } else {
            request = requestBuilder.url(url).build();
        }

        return request;
    }

    /**
     * 执行异步请求
     * @param client OkHttpClient实例
     * @param request Request实例
     * @param reMap 返回结果Map
     * @param url 请求URL
     * @param upResponseBody 上游响应体
     * @param requestAfterGroovy 请求后Groovy脚本
     */
    private static void executeAsyncRequest(OkHttpClient client, Request request,
                                            Map<String, Object> reMap, HttpUrl url,
                                            String upResponseBody, String requestAfterGroovy) {
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                log.error("失败回调 onFailure: " + e.getMessage());
                if (shouldRetry(e, null, serversLoadTimes.get(), DEFAULT_MAX_RETRY_TIMES)) {
                    serversLoadTimes.set(serversLoadTimes.get() + 1);
                    log.warn("重连次数: 【{}】 ", serversLoadTimes.get());
                    client.newCall(call.request()).enqueue(this);
                } else {
                    log.error("请求失败，无法重试: " + e.getMessage(), e);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (Strings.isNotEmpty(requestAfterGroovy)) {
                        Object[] executeAfterGroovy = new Object[]{upResponseBody, response};
                        executeGroovy(executeAfterGroovy, requestAfterGroovy);
                    }

                    if (response.isSuccessful()) {
                        if (null != response.body()) {
                            String responseBody = response.body().string();
                            reMap.put("mag", responseBody);
                            log.info("请求后返回数据： " + responseBody);
                        }
                    } else {
                        log.error("请求接口地址失败：" + url + "，状态码：" + response.code());
                    }
                } finally {
                    if (response.body() != null) {
                        response.body().close();
                    }
                }
            }
        });
    }

    /**
     * 执行同步请求
     * @param client OkHttpClient实例
     * @param request Request实例
     * @param reMap 返回结果Map
     * @param url 请求URL
     * @param upResponseBody 上游响应体
     * @param requestAfterGroovy 请求后Groovy脚本
     * @return 请求结果Map
     * @throws IOException IO异常
     */
    private static Map<String, Object> executeSyncRequest(OkHttpClient client, Request request,
                                                          Map<String, Object> reMap, HttpUrl url,
                                                          String upResponseBody, String requestAfterGroovy) throws IOException {
        Response response = client.newCall(request).execute();
        try {
            if (Strings.isNotEmpty(requestAfterGroovy)) {
                Object[] executeAfterGroovy = new Object[]{upResponseBody, response};
                executeGroovy(executeAfterGroovy, requestAfterGroovy);
            }

            if (response.isSuccessful()) {
                if (null != response.body()) {
                    String responseBody = response.body().string();
                    reMap.put("mag", responseBody);
                    log.info("请求后返回数据： " + responseBody);
                }
            } else {
                log.error("请求接口地址失败：" + url + "，状态码：" + response.code());
            }

            reMap.put("success", true);
            reMap.put("url", url);
            return reMap;
        } finally {
            if (response.body() != null) {
                response.body().close();
            }
        }
    }

    /**
     * 判断是否应该重试
     * @param e 异常对象
     * @param response 响应对象
     * @param retryCount 当前重试次数
     * @param maxRetries 最大重试次数
     * @return 是否应该重试
     */
    private static boolean shouldRetry(IOException e, Response response, int retryCount, int maxRetries) {
        if (retryCount >= maxRetries) {
            return false;
        }

        // 网络超时重试
        if (e instanceof SocketTimeoutException) {
            return true;
        }

        // 特定HTTP状态码重试
        if (response != null && RETRYABLE_STATUS_CODES.contains(response.code())) {
            return true;
        }

        return false;
    }

    /**
     * 执行Groovy脚本
     *
     * @param objects Groovy接收的参数
     * @param groovy  要执行的脚本
     * @return 执行结果
     */
    private static Object executeGroovy(Object[] objects, String groovy) {
        if (Strings.isNotEmpty(groovy)) {
            try {
                Class<?> scriptClass = scriptCache.computeIfAbsent(groovy, g -> {
                    try (GroovyClassLoader groovyClassLoader = new GroovyClassLoader()) {
                        return groovyClassLoader.parseClass(g);
                    } catch (Exception e) {
                        throw new RuntimeException("脚本编译失败: " + e.getMessage(), e);
                    }
                });

                Script script = InvokerHelper.createScript(scriptClass, new Binding());
                log.debug("执行脚本: {}", groovy);
                Object executeResult = script.invokeMethod("execute", objects);
                log.debug("执行后返回的值：{}", executeResult);
                return executeResult;
            } catch (Exception e) {
                log.error("Groovy脚本执行失败", e);
                throw new RuntimeException("脚本执行异常: " + e.getMessage(), e);
            }
        }
        return null;
    }

    /**
     * 参数校验
     * @param requestSend 请求参数对象
     */
    private static void validateRequestSend(HttpRequestSend requestSend) {
        if (requestSend == null) {
            throw new IllegalArgumentException("requestSend 不能为空");
        }

        if (Strings.isEmpty(requestSend.getRequestUrl())) {
            throw new IllegalArgumentException("请求URL不能为空");
        }

        try {
            HttpUrl.parse(requestSend.getRequestUrl());
        } catch (Exception e) {
            throw new IllegalArgumentException("请求URL格式不正确: " + requestSend.getRequestUrl());
        }
    }

    /**
     * 示例用法
     */
    public static void main(String[] args) throws IOException {
        // 同步请求示例
        HttpRequestSend httpRequestSend = new HttpRequestSend();
        httpRequestSend.setRequestUrl("http://localhost:8080/renren-admin/oatools/system/page?order=&orderField=&page=1&limit=10");
        httpRequestSend.setRequestType("GET");
        httpRequestSend.setRequestHeaders("{\"token\":\"6f0e63036af48f2417a327ff108d79b4\"}");
        httpRequestSend.setIsAsyn(0); // 同步请求

        try {
            Map<String, Object> result = requestSend(null, httpRequestSend);
            System.out.println("同步请求结果: " + result);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 异步请求示例
        HttpRequestSend asyncRequestSend = new HttpRequestSend();
        asyncRequestSend.setRequestUrl("http://localhost:8080/renren-admin/oatools/system/page?order=&orderField=&page=1&limit=10");
        asyncRequestSend.setRequestType("GET");
        asyncRequestSend.setRequestHeaders("{\"token\":\"6f0e63036af48f2417a327ff108d79b4\"}");
        asyncRequestSend.setIsAsyn(1); // 异步请求

        try {
            Map<String, Object> result = requestSend(null, asyncRequestSend);
            System.out.println("异步请求已发送: " + result);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}